{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 作业要求：\n",
    "本次作业分两部分：\n",
    "1. 客观题；\n",
    "2. 代码填空题；\n",
    "\n",
    "\n",
    "两部分作业共计100分，其中：\n",
    "\n",
    "客观题：10道题，5分/个，共50分；\n",
    "代码填空题：运行得到符合要求的曲线则计50分，否则该部分得0分。\n",
    "\n",
    "\n",
    "## 第一部分：客观题\n",
    "链接：[https://ks.wjx.top/jq/43297274.aspx](https://ks.wjx.top/jq/43297274.aspx)\n",
    "\n",
    "要求：\n",
    "1. 大家必须于**7月30日（周二）22:00**前完成作业并提交，晚于该时间将无法作答，该部分计0分。\n",
    "## 第二部分：代码填空题\n",
    "要求：\n",
    "1. 补全下面代码中的填空部分；\n",
    "2. 补全填空后，要求代码能够运行，不报错，且有运行曲线输出，且输出的曲线和代码讲解中给出的曲线相符，即曲线趋势相似；\n",
    "3. **切记不要**清空输出，否则老师无法看到你的输出空间的运行结果，按没有完成作业来论，该部分作业计0分；\n",
    "4. 大家于**7月30日（周二）22:00**前完成作业（即运行得到符合要求的曲线），助教将从该时间起判作业，如果看不到大家的曲线，该部分作业计0分。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Archive:  data/data9911/data_images.zip\r\n",
      "  inflating: data_images/datasets/data.txt  \r\n",
      "  inflating: data_images/images/.png  \r\n",
      "  inflating: data_images/images/FC.png  \r\n",
      "  inflating: data_images/images/ML_process.png  \r\n",
      "  inflating: data_images/images/gradient_descent.png  \r\n",
      "  inflating: data_images/images/plt.png  \r\n",
      "  inflating: data_images/images/result.png  \r\n"
     ]
    }
   ],
   "source": [
    "#因为本次实验除了代码还有许多知识点讲解。\r\n",
    "#知识点是以markdown形式展现的，含有许多图片。\r\n",
    "#运行完这条命令，并刷新一下本页面，本实验中的图片就可以展现出来了。\r\n",
    "#这条命令只需要运行一遍就可以了。\r\n",
    "!DATA_PATH=data/data9911/ && NEW_NAME=$(find -name *[0-9].ipynb) && NEW_NAME=${NEW_NAME%.*} && NEW_NAME=${NEW_NAME#./} && unzip -o ${DATA_PATH}data_images.zip  && cp -rf data_images/. ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PaddlePaddle实现线性回归\n",
    "\n",
    "欢迎大家来到这次实验！在本次实验中我们将使用PaddlePaddle深度学习框架来搭建一个简单的线性回归模型，并利用这一模型来解决一个大家都关心的实际问题————预测你的储蓄（也就是money）（在某地区）可以购买多大面积的房子。并且在学习本次实验课的过程中，我还会带领大家学习到机器学习的若干重要概念，掌握一个机器学习预测案例的基本流程。当然了，有机器学习基础的同学就是回忆喽！\n",
    "\n",
    "**本节实验课你将学会**\n",
    "\n",
    "- 机器学习的基本概念：假设函数、损失函数、优化算法\n",
    "- 数据怎么进行归一化处理\n",
    "- paddlepaddle深度学习框架的一些基本知识\n",
    "- 如何用paddlepaddle深度学习框架搭建全连接神经网络，实现线性回归（你将会发现用paddlepaddle搭建神经网络是多么的简单）\n",
    "\n",
    "大家期待不期待？\n",
    "\n",
    "在实验开始之前，我们先对机器学习的相关概念进行学习、复习和回顾！\n",
    "\n",
    "**机器学习回顾**\n",
    "\n",
    "机器学习是怎么一个工作过程呢？  \n",
    "是这样的：  \n",
    "我们首先输入训练数据集，利用特定的机器学习方法建立估计函数，并进行训练。训练后得到一个模型。然后向这一模型输入测试数据集，函数有能力对没有见过的数据进行正确估计，这就是机器学习的过程。\n",
    "<img src=\"images/ML_process.png\" style=\"width:300px;height:180px;\">\n",
    "\n",
    "**线性回归的基本概念**  \n",
    "再来回顾一下线性回归的一些知识：  \n",
    "线性回归是机器学习中最简单也是最重要的模型之一，其模型建立同样遵循上图流程：获取数据、数据预处理、训练模型、应用模型。\n",
    "\n",
    "回归模型可以理解为：存在一个点集，用一条曲线去拟合它分布的过程。如果拟合曲线是一条直线，则称为线性回归。如果是一条二次曲线，则被称为二次回归。线性回归是回归模型中最简单的一种。\n",
    "\n",
    "在线性回归中有几个基本的概念需要掌握：\n",
    "- 假设函数（Hypothesis Function）\n",
    "- 损失函数（Loss Function）\n",
    "- 优化算法（Optimization Algorithm）\n",
    "\n",
    "**假设函数**：\n",
    "\n",
    "假设函数是指，用数学的方法描述自变量和因变量之间的关系，它们之间可以是一个线性函数或非线性函数。\n",
    "在本次线性回顾模型中，我们的假设函数为 $\\hat{Y}= aX_1+b$ ，其中，$\\hat{Y}$表示模型的预测结果（预测房价），用来和真实的Y区分。模型要学习的参数即：a,b。\n",
    "\n",
    "**损失函数**：\n",
    "\n",
    "损失函数是指，用数学的方法衡量假设函数预测结果与真实值之间的误差。这个差距越小预测越准确，而算法的任务就是使这个差距越来越小。\n",
    "\n",
    "建立模型后，我们需要给模型一个优化目标，使得学到的参数能够让预测值$\\hat{Y}$尽可能地接近真实值Y。输入任意一个数据样本的目标值$y_i$和模型给出的预测值$\\hat{Y_i}$，损失函数输出一个非负的实值。这个实值通常用来反映模型误差的大小。\n",
    "\n",
    "对于线性模型来讲，最常用的损失函数就是均方误差（Mean Squared Error， MSE）。\n",
    "$$MSE=\\frac{1}{n}\\sum_{i=1}^{n}(\\hat{Y_i}-Y_i)^2$$\n",
    "即对于一个大小为n的测试集，MSE是n个数据预测结果误差平方的均值。\n",
    "\n",
    "**优化算法**：\n",
    "\n",
    "在模型训练中优化算法也是至关重要的，它决定了一个模型的精度和运算速度。本章的线性回归实例中主要使用了梯度下降法进行优化。\n",
    "\n",
    "**梯度下降**是深度学习中非常重要的概念，值得庆幸的是它也十分容易理解。损失函数$J(w,b)$可以理解为变量$w$和$b$的函数。观察下图，垂直轴表示损失函数的值，两个水平轴分别表示变量$w$和$b$。实际上，可能是更高维的向量，但是为了方便说明，在这里假设$w$和$b$都是一个实数。算法的最终目标是找到损失函数的最小值。而这个寻找过程就是不断地微调变量$w$和$b$的值，一步一步地试出这个最小值。而试的方法就是沿着梯度方向逐步移动。本例中让图中的圆点表示损失函数的某个值，那么梯度下降就是让圆点沿着曲面下降，直到取到最小值或逼近最小值。\n",
    "\n",
    "因为是凸函数，所以无论初始化在曲面上的哪一点，最终都会收敛到同一点或者相近的点。\n",
    "\n",
    "<img src=\"images/gradient_descent.png\" style=\"width:380px;height:180px;\">\n",
    "\n",
    "\n",
    "现在，让我们正式进入实验吧！\n",
    "\n",
    "# 1 - 引用库\n",
    "\n",
    "首先载入需要用到的库，它们分别是：\n",
    "\n",
    "- numpy：NumPy是Python语言的一个扩展程序库。支持高端大量的维度数组与矩阵运算，此外也针对数组运算提供大量的数学函数库。NumPy的核心功能是\"ndarray\"(即n-dimensional array，多维数组)数据结构。\n",
    "- matplotlib.pyplot：用于生成图，在验证模型准确率和展示成本变化趋势时会使用到\n",
    "- paddle.fluid：引入PaddlePaddle深度学习框架的fluid版本库；\n",
    "- pandas:Pandas是python第三方库，提供高性能易用数据类型和分析工具，Pandas基于Numpy实现，常与Numpy和Matplotlib一同使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "\n",
    "import math\n",
    "import sys\n",
    "\n",
    "#%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2 - 数据预处理\r\n",
    "\r\n",
    "本次数据集使用的是2016年12月份某市某地区的房价分布。为了简化模型，假设影响房价的因素只有房屋面积，因此数据集只有两列，以TXT的形式储存。下面我们打开数据集来看一看。  \r\n",
    "\r\n",
    "当真实数据被收集到后，它们往往不能直接使用。  \r\n",
    "例如本次数据集使用了某地区的房价分布，为了简化模型数据只有两维，并没有标出来每一列代表什么，其实分别是房屋面积与房屋价格。可以看到房价与房屋面积之间存在一种关系，这种关系究竟是什么，就是本次预测想要得到的结论。可以首先以表格的形式输出数据的前五行看一下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>房屋面积</th>\n",
       "      <th>房价</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>98.87</td>\n",
       "      <td>599.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>68.74</td>\n",
       "      <td>450.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>89.24</td>\n",
       "      <td>440.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>129.19</td>\n",
       "      <td>780.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>61.64</td>\n",
       "      <td>450.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     房屋面积     房价\n",
       "0   98.87  599.0\n",
       "1   68.74  450.0\n",
       "2   89.24  440.0\n",
       "3  129.19  780.0\n",
       "4   61.64  450.0"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "colnames = ['房屋面积']+['房价']\n",
    "print_data = pd.read_csv('./datasets/data.txt',names = colnames)\n",
    "print_data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一般拿到一组数据后，第一个要处理的是数据类型不同的问题。如果各维属性中有离散值和连续值，就必须对离散值进行处理。\r\n",
    "\r\n",
    "离散值虽然也常使用类似0、1、2这样的数字表示，但是其含义与连续值是不同的，因为这里的差值没有实际意义。例如，我们用0、1、2来分别表示红色、绿色和蓝色的话，我们并不能因此说“蓝色和红色”比“绿色和红色”的距离更远。通常对有d个可能取值的离散属性，我们会将它们转为d个取值为0或1的二值属性或者将每个可能取值映射为一个多维向量。\r\n",
    "\r\n",
    "不过就这里而言，数据中没有离散值，就不用考虑这个问题了。\r\n",
    "\r\n",
    "** 归一化 **\r\n",
    "\r\n",
    "观察一下数据的分布特征，一般而言，如果样本有多个属性，那么各维属性的取值范围差异会很大，这就要用到一个常见的操作-归一化（normalization）了。归一化的目标是把各维属性的取值范围放缩到差不多的区间，例如[-0.5, 0.5]。这里我们使用一种很常见的操作方法：减掉均值，然后除以原取值范围。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "虽然本次房价预测模型中，输入属性只有房屋面积，不存在取值范围差异问题，但由于归一化的各种优点，我们仍选择对其进行归一化操作。\n",
    "\n",
    "**练习：**\n",
    "\n",
    "完成下列代码，实现房屋面积的归一化。\n",
    "\n",
    "输出归一化后的房价最大值，判断操作是否正确。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the raw area : 199.96\n",
      "94.64454022988511\n",
      "[[ 2.64305984e-02  5.99000000e+02]\n",
      " [-1.62035030e-01  4.50000000e+02]\n",
      " [-3.38058437e-02  4.40000000e+02]\n",
      " ...\n",
      " [-3.53070634e-02  7.35000000e+02]\n",
      " [-2.19644337e-01  3.60000000e+02]\n",
      " [ 1.47335946e-02  6.00000000e+02]]\n",
      "normalization: 0.6587568635148239\n"
     ]
    }
   ],
   "source": [
    "# coding = utf-8 #\n",
    "global x_raw,train_data,test_data\n",
    "data = np.loadtxt('./datasets/data.txt',delimiter = ',')\n",
    "x_raw = data.T[0].copy() \n",
    "\n",
    "#axis=0,表示按列计算\n",
    "#data.shape[0]表示data中一共有多少列\n",
    "maximums, minimums, avgs = data.max(axis=0), data.min(axis=0), data.sum(axis=0)/data.shape[0]\n",
    "print(\"the raw area :\",data[:,0].max(axis = 0))\n",
    "print(avgs[0])\n",
    "#归一化，data[:,i]表示第i列的元素\n",
    "\n",
    "### START CODE HERE ### (≈ 3 lines of code)\n",
    "data[:,0] = (data[:,0] - avgs[0])/(maximums[0]-minimums[0])\n",
    "\n",
    "\n",
    "\n",
    "### END CODE HERE ###\n",
    "print(data)\n",
    "print('normalization:',data[:,0].max(axis = 0))\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "期望输出：normalization: 0.6587568635148239"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "基本上所有的数据在拿到后都必须进行归一化，至少有以下3条原因：\n",
    "\n",
    "1.过大或过小的数值范围会导致计算时的浮点上溢或下溢。\n",
    "\n",
    "2.不同的数值范围会导致不同属性对模型的重要性不同（至少在训练的初始阶段如此），而这个隐含的假设常常是不合理的。这会对优化的过程造成困难，使训练时间大大加长。\n",
    "\n",
    "3.很多的机器学习技巧/模型（例如L1，L2正则项，向量空间模型-Vector Space Model）都基于这样的假设：所有的属性取值都差不多是以0为均值且取值范围相近的。\n",
    "\n",
    "** 数据集分割 **\n",
    "\n",
    "将原始数据处理为可用数据后，为了评估模型的好坏，我们将数据分成两份：训练集和测试集。\n",
    "- 训练集数据用于调整模型的参数，即进行模型的训练，模型在这份数据集上的误差被称为训练误差；\n",
    "- 测试集数据被用来测试，模型在这份数据集上的误差被称为测试误差。\n",
    "\n",
    "我们训练模型的目的是为了通过从训练数据中找到规律来预测未知的新数据，所以测试误差是更能反映模型表现的指标。分割数据的比例要考虑到两个因素：更多的训练数据会降低参数估计的方差，从而得到更可信的模型；而更多的测试数据会降低测试误差的方差，从而得到更可信的测试误差。我们这个例子中设置的分割比例为8:2。\n",
    "\n",
    "**练习：**\n",
    "\n",
    "补全下列代码，完成训练集与测试集的分割。输出数据大小，验证分割是否正确。\n",
    "\n",
    "- 提示：\n",
    "\n",
    "    A[:x] 表示取A中的前x个元素\n",
    "    \n",
    "    A[x:]表示从第x+1个元素开始取到最后一个元素\n",
    "    \n",
    "期望输出：  \n",
    "\n",
    "870  \n",
    "696\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "870\n",
      "696\n"
     ]
    }
   ],
   "source": [
    "ratio = 0.8\n",
    "offset = int(data.shape[0]*ratio)\n",
    "\n",
    "### START CODE HERE ### (≈ 2 lines of code)\n",
    "train_data = data[:offset] \n",
    "test_data  = data[offset:]\n",
    "### END CODE HERE ###\n",
    "\n",
    "print(len(data))\n",
    "print(len(train_data))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3 - 定义reader\r\n",
    "\r\n",
    "构造read_data()函数，来读取训练数据集train_set或者测试数据集test_set。它的具体实现是在read_data()函数内部构造一个reader()，使用yield关键字来让reader()成为一个Generator（生成器），注意，yield关键字的作用和使用方法类似return关键字，不同之处在于yield关键字可以构造生成器（Generator）。虽然我们可以直接创建一个包含所有数据的列表，但是由于内存限制，我们不可能创建一个无限大的或者巨大的列表，并且很多时候在创建了一个百万数量级别的列表之后，我们却只需要用到开头的几个或几十个数据，这样造成了极大的浪费，而生成器的工作方式是在每次循环时计算下一个值，不断推算出后续的元素，不会创建完整的数据集列表，从而节约了内存使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def read_data(data_set):\n",
    "    \"\"\"\n",
    "    一个reader\n",
    "    Args：\n",
    "        data_set -- 要获取的数据集\n",
    "    Return：\n",
    "        reader -- 用于获取训练集及其标签的生成器generator\n",
    "    \"\"\"\n",
    "    def reader():\n",
    "        \"\"\"\n",
    "        一个reader\n",
    "        Args：\n",
    "        Return：\n",
    "            data[:-1],data[-1:] --使用yield返回生成器\n",
    "                data[:-1]表示前n-1个元素，也就是训练数据，\n",
    "                data[-1:]表示最后一个元素，也就是对应的标签\n",
    "        \"\"\"\n",
    "        for data in data_set:\n",
    "            yield data[:-1],data[-1:]\n",
    "    return reader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test_array for read_data:\n",
      "([10], [100])\n",
      "([20], [200])\n"
     ]
    }
   ],
   "source": [
    "#测试reader\n",
    "\n",
    "test_array = ([10,100],[20,200])\n",
    "print(\"test_array for read_data:\")\n",
    "for value in read_data(test_array)():\n",
    "    print(value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来我们定义了用于训练的数据提供器。提供器每次读入一个大小为BATCH_SIZE的数据批次。如果用户希望加一些随机性，它可以同时定义一个批次大小和一个缓存大小。这样的话，每次数据提供器会从缓存中随机读取批次大小那么多的数据。我们都可以通过batch_size进行设置，这个大小一般是2的N次方。\r\n",
    "\r\n",
    "关于参数的解释如下：\r\n",
    "\r\n",
    "- paddle.reader.shuffle(read_data(train_data),  buf_size=500)表示从read_data(train_data)中读取了buf_size=500大小的数据并打乱顺序\r\n",
    "- paddle.batch(reader(), batch_size=BATCH_SIZE)表示从打乱的数据中再取出BATCH_SIZE=20大小的数据进行一次迭代训练\r\n",
    "\r\n",
    "如果buf_size设置的数值大于数据集本身，就直接把整个数据集打乱顺序；如果buf_size设置的数值小于数据集本身，就按照buf_size的大小打乱顺序。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "BATCH_SIZE = 32\n",
    "\n",
    "# 设置训练reader\n",
    "train_reader = paddle.batch(\n",
    "    paddle.reader.shuffle(\n",
    "        read_data(train_data), \n",
    "        buf_size=500),\n",
    "    batch_size=BATCH_SIZE)\n",
    "\n",
    "#设置测试 reader\n",
    "test_reader = paddle.batch(\n",
    "    paddle.reader.shuffle(\n",
    "        read_data(test_data), \n",
    "        buf_size=500),\n",
    "    batch_size=BATCH_SIZE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4 - 训练过程\r\n",
    "\r\n",
    "\r\n",
    "完成了数据的预处理工作并构造了read_data()来读取数据，接下来将进入模型的训练过程，使用PaddlePaddle来定义构造可训练的线性回归模型，关键步骤如下：\r\n",
    "\r\n",
    "- 配置网络结构和设置参数\r\n",
    "    - 配置网络结构\r\n",
    "    - 定义损失函数cost\r\n",
    "    - 定义执行器(参数随机初始化) \r\n",
    "    - 定义优化器optimizer\r\n",
    "\r\n",
    "- 模型训练\r\n",
    "\r\n",
    "- 预测\r\n",
    "\r\n",
    "- 绘制拟合图像\r\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\r\n",
    "**定义运算场所：**\r\n",
    "\r\n",
    "首先进行最基本的运算场所定义，在 fluid 中使用 place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() 来进行初始化：\r\n",
    "\r\n",
    "- place 表示fluid program的执行设备，常见的有 fluid.CUDAPlace(0) 和 fluid.CPUPlace()\r\n",
    "- use_cuda = False 表示不使用 GPU 进行加速训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#使用CPU或者GPU训练\n",
    "use_cuda = True\n",
    "place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**配置网络结构和设置参数：**\r\n",
    "\r\n",
    "**配置网络结构：**\r\n",
    "\r\n",
    "\r\n",
    "线性回归的模型其实就是一个采用线性激活函数（linear activation）的全连接层（fully-connected layer，fc_layer），因此在Peddlepeddle中利用全连接层模型构造线性回归，这样一个全连接层就可以看做是一个简单的神经网络，只包含输入层和输出层即可。本次的模型由于只有一个影响参数，因此输入只含一个$X_0$。\r\n",
    "\r\n",
    "<img src=\"images/FC.png\" style=\"width:380px;height:180px;\">\r\n",
    "\r\n",
    "接下来就让我们利用PaddlePaddle提供的接口，搭建我们自己的网络吧！\r\n",
    "\r\n",
    "**输入层**  \r\n",
    "我们可以用 x = fluid.layers.data(name='x', shape=[1], dtype='float32')来表示数据的一个输入层，其中name属性的名称为\"x\"，数据的shape为一维向量，这是因为本次所用的房价数据集的每条数据只有1个属性，所以shape=1。\r\n",
    "\r\n",
    "\r\n",
    "\r\n",
    "**输出层**  \r\n",
    "用y_predict = fluid.layers.fc(input=x, size=1, act=None)来表示输出层：其中paddle.layer.fc表示全连接层，input=x表示该层出入数据为x，size=1表示该层有一个神经元，在Fluid版本中使用的激活函数不再是调用一个函数了，而是传入一个字符串就可以，比如：act='relu'就表示使用relu激活函数。act=None表示激活函数为线性激活函数。\r\n",
    "\r\n",
    "\r\n",
    "**标签层**\r\n",
    "\r\n",
    "用y = fluid.layers.data(name='y', shape=[1], dtype='float32')来表示标签数据，名称为y，有时我们名称不用y而用label。数据类型为一维向量。\r\n",
    "\r\n",
    "**练习** 根据提示，完成下面代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 输入层，fluid.layers.data表示数据层,name=’x’：名称为x,输出类型为tensor\n",
    "# shape=[1]:数据为1维向量\n",
    "# dtype='float32'：数据类型为float32\n",
    "### START CODE HERE ### (≈ 1 lines of code)\n",
    "x = fluid.layers.data(name='x', shape=[1], dtype='float32')\n",
    "\n",
    "### END CODE HERE ###\n",
    "\n",
    "\n",
    "# 标签数据，fluid.layers.data表示数据层,name=’y’：名称为y,输出类型为tensor\n",
    "# shape=[1]:数据为1维向量\n",
    "### START CODE HERE ### (≈ 1 lines of code)\n",
    "y = fluid.layers.data(name='y', shape=[1], dtype='float32')\n",
    "\n",
    "### END CODE HERE ###\n",
    "\n",
    "# 输出层，fluid.layers.fc表示全连接层，input=x: 该层输入数据为x\n",
    "# size=1：神经元个数，act=None：激活函数为线性函数\n",
    "y_predict = fluid.layers.fc(input=x, size=1, act=None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**定义损失函数:**\r\n",
    "\r\n",
    "PaddlePaddle提供了很多的损失函数的接口，比如交叉熵损失函数(cross_entropy)。因为本项目是一个线性回归任务，所以我们使用的是均方差损失函数。可以调用fluid.layers.square_error_cost(input= ,laybel= )实现方差计算。因为fluid.layers.square_error_cost(input= ,laybel= )求的是一个Batch的损失值，所以我们还要通过调用fluid.layers.mean(loss)对方差求平均。\r\n",
    "\r\n",
    "将输入定义为 **房价预测值**，label定义为 **标签数据**。进而计算损失值。\r\n",
    "\r\n",
    "**练习:** 完成下面代码\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 定义损失函数为均方差损失函数,并且求平均损失，返回值名称为avg_loss\n",
    "### START CODE HERE ### (≈ 2 lines of code)\n",
    "avg_loss = fluid.layers.mean(fluid.layers.square_error_cost(input=y_predict, label=y))\n",
    "\n",
    "### END CODE HERE ###"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**定义执行器(参数随机初始化):**\r\n",
    "\r\n",
    "首先定义执行器，fulid使用了一个C++类Executor用于运行一个程序，Executor类似一个解析器，Fluid将会使用这样一个解析器来训练和测试模型。\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "exe = fluid.Executor(place)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**配置训练程序:**\r\n",
    "\r\n",
    "①全局主程序main program。该主程序用于训练模型。\r\n",
    "\r\n",
    "②全局启动程序startup_program。\r\n",
    "\r\n",
    "③测试程序test_program。用于模型测试。\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "main_program = fluid.default_main_program() # 获取默认/全局主函数\n",
    "startup_program = fluid.default_startup_program() # 获取默认/全局启动程序\n",
    "\n",
    "#克隆main_program得到test_program\n",
    "#有些operator在训练和测试之间的操作是不同的，例如batch_norm，使用参数for_test来区分该程序是用来训练还是用来测试\n",
    "#该api不会删除任何操作符,请在backward和optimization之前使用\n",
    "test_program = main_program.clone(for_test=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**优化方法:**\n",
    "\n",
    "损失函数定义确定后，需要定义参数优化方法。为了改善模型的训练速度以及效果，学术界先后提出了很多优化算法，包括： Momentum、RMSProp、Adam 等，已经被封装在fluid内部，读者可直接调用。本次可以用 fluid.optimizer.SGD(learning_rate= ) 使用随机梯度下降的方法优化，其中learning_rate表示学习率，大家可以自己尝试修改。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "optimizer is ready\n"
     ]
    }
   ],
   "source": [
    "# 创建optimizer，更多优化算子可以参考 fluid.optimizer()\n",
    "learning_rate = 0.01\n",
    "sgd_optimizer = fluid.optimizer.SGD(learning_rate)\n",
    "sgd_optimizer.minimize(avg_loss)\n",
    "print(\"optimizer is ready\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**训练模型:**\r\n",
    "\r\n",
    "上述内容进行了模型初始化、网络结构的配置并创建了训练函数、硬件位置、优化方法，接下来利用上述配置进行模型训练。\r\n",
    "\r\n",
    "\r\n",
    "**创建训练过程:**\r\n",
    "\r\n",
    "训练需要有一个训练程序和一些必要参数，并构建了一个获取训练过程中测试误差的函数。必要参数有executor,program,reader,feeder,fetch_list，executor表示之前创建的执行器，program表示执行器所执行的program，是之前创建的program，如果该项参数没有给定的话则默认使用defalut_main_program，reader表示读取到的数据，feeder表示前向输入的变量，fetch_list表示用户想得到的变量或者命名的结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# For training test cost\n",
    "def train_test(executor, program, reader, feeder, fetch_list):\n",
    "    accumulated = 1 * [0]\n",
    "    count = 0\n",
    "    for data_test in reader():\n",
    "        outs = executor.run(\n",
    "            program=program, feed=feeder.feed(data_test), fetch_list=fetch_list)\n",
    "        accumulated = [x_c[0] + x_c[1][0] for x_c in zip(accumulated, outs)]  # 累加测试过程中的损失值\n",
    "        count += 1 # 累加测试集中的样本数量\n",
    "    return [x_d / count for x_d in accumulated] # 计算平均损失"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#定义模型保存路径：\n",
    "#params_dirname用于定义模型保存路径。\n",
    "params_dirname = \"easy_fit_a_line.inference.model\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**训练主循环**\r\n",
    "\r\n",
    "我们构建一个循环来进行训练，直到训练结果足够好或者循环次数足够多。 如果训练迭代次数满足参数保存的迭代次数，可以把训练参数保存到params_dirname。 设置训练主循环"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "#用于画图展示训练cost\n",
    "from paddle.utils.plot import Ploter\n",
    "train_prompt = \"Train cost\"\n",
    "test_prompt = \"Test cost\"\n",
    "plot_prompt = Ploter(train_prompt, test_prompt)\n",
    "step = 0\n",
    "\n",
    "# 训练主循环\n",
    "feeder = fluid.DataFeeder(place=place, feed_list=[x, y])\n",
    "exe.run(startup_program)\n",
    "\n",
    "exe_test = fluid.Executor(place)\n",
    "\n",
    "#num_epochs=100表示迭代训练100次后停止训练。\n",
    "num_epochs = 100\n",
    "\n",
    "for pass_id in range(num_epochs):\n",
    "    for data_train in train_reader():\n",
    "        avg_loss_value, = exe.run(main_program,\n",
    "                                  feed=feeder.feed(data_train),\n",
    "                                  fetch_list=[avg_loss])\n",
    "        if step % 10 == 0:  # 每10个批次记录并输出一下训练损失\n",
    "            plot_prompt.append(train_prompt, step, avg_loss_value[0])\n",
    "            plot_prompt.plot()\n",
    "            #print(\"%s, Step %d, Cost %f\" %(train_prompt, step, avg_loss_value[0]))\n",
    "        if step % 100 == 0:  # 每100批次记录并输出一下测试损失\n",
    "            test_metics = train_test(executor=exe_test,\n",
    "                                     program=test_program,\n",
    "                                     reader=test_reader,\n",
    "                                     fetch_list=[avg_loss.name],\n",
    "                                     feeder=feeder)\n",
    "            plot_prompt.append(test_prompt, step, test_metics[0])\n",
    "            plot_prompt.plot()\n",
    "            #print(\"%s, Step %d, Cost %f\" %(test_prompt, step, test_metics[0]))\n",
    "            \n",
    "            if test_metics[0] < 10.0: # 如果准确率达到要求，则停止训练\n",
    "                break\n",
    "\n",
    "        step += 1\n",
    "\n",
    "        if math.isnan(float(avg_loss_value[0])):\n",
    "            sys.exit(\"got NaN loss, training failed.\")\n",
    "\n",
    "        #保存训练参数到之前给定的路径中\n",
    "        if params_dirname is not None:\n",
    "            fluid.io.save_inference_model(params_dirname, ['x'], [y_predict], exe)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "得到的cost函数变化图像大致应是一个收敛的结果：\r\n",
    "<img src='images/result.png' style = \"width:400px;height:300px;\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4 - 预测\r\n",
    "\r\n",
    "\r\n",
    "预测器会从params_dirname中读取已经训练好的模型，来对从未遇见过的数据进行预测。  \r\n",
    "通过fluid.io.load_inference_model，预测器会从params_dirname中读取已经训练好的模型，来对从未遇见过的数据进行预测。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "infer_exe = fluid.Executor(place)\n",
    "inference_scope = fluid.core.Scope()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**预测**\r\n",
    "\r\n",
    "预测器会从params_dirname中读取已经训练好的模型，来对从未遇见过的数据进行预测。\r\n",
    "\r\n",
    "- tensor_x:生成batch_size个[0,1]区间的随机数，以 tensor 的格式储存\r\n",
    "- results：预测对应 tensor_x 面积的房价结果\r\n",
    "- raw_x:由于数据处理时我们做了归一化操作，为了更直观的判断预测是否准确，将数据进行反归一化，得到随机数对应的原始数据。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor_x is : [[0.01975913]\n",
      " [0.1009222 ]]\n",
      "the area is: [[ 97.80343]\n",
      " [110.77897]]\n",
      "infer results:  [[629.6942 ]\n",
      " [708.95013]]\n"
     ]
    }
   ],
   "source": [
    "with fluid.scope_guard(inference_scope):\n",
    "    [inference_program, feed_target_names, fetch_targets\n",
    "     ] = fluid.io.load_inference_model(params_dirname, infer_exe) # 载入预训练模型\n",
    "\n",
    "\n",
    "    batch_size = 2\n",
    "    tensor_x = np.random.uniform(0, 1, [batch_size, 1]).astype(\"float32\")\n",
    "    \n",
    "    print(\"tensor_x is :\" ,tensor_x )\n",
    "    results = infer_exe.run(\n",
    "        inference_program,\n",
    "        feed={feed_target_names[0]: tensor_x},\n",
    "        fetch_list=fetch_targets) # 进行预测\n",
    "    raw_x = tensor_x*(maximums[0]-minimums[0])+avgs[0]\n",
    "    print(\"the area is:\",raw_x)\n",
    "    print(\"infer results: \", results[0])\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此处应得到一组预测结果：\r\n",
    "\r\n",
    "('the area is:', array([[####],\r\n",
    "\r\n",
    "       [####]], dtype=float32))\r\n",
    "       \r\n",
    "('infer results: ', array([[####],\r\n",
    "\r\n",
    "       [####]], dtype=float32))\r\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "根据线性模型的原理，补全输出公式，计算a和b的值\n",
    "\n",
    "- 提示：已知两点求直线方程\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6.108102 32.300903\n"
     ]
    }
   ],
   "source": [
    "\n",
    "a = (results[0][0][0] - results[0][1][0]) / (raw_x[0][0]-raw_x[1][0])\n",
    "b = (results[0][0][0] - a * raw_x[0][0])\n",
    "\n",
    "print(a,b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "预测结果应为：a=6.7,b=-24.42(每次训练结果取随机数，因此得到的结果可能会有一点点偏差，但大致应在这个范围之间）,因此本次模型得到的房屋面积与房价之间的拟合函数为$y=6.7x-24.42$。其中y为预测的房屋价格，x为房屋面积，根据这个公式可以推断：如果有500万的预算，想在该地区购房，房屋面积大概为$\\frac{500-(-24.42)}{6.7}=78(m^2)$。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**（5）绘制拟合图像 **\n",
    "\n",
    "通过训练，本次线性回归模型输出了一条拟合的直线，想要直观的判断模型好坏可将拟合直线与数据的图像绘制出来。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def plot_data(data):\n",
    "    x = data[:,0]\n",
    "    y = data[:,1]\n",
    "    y_predict = x*a + b\n",
    "    plt.scatter(x,y,marker='.',c='r',label='True')\n",
    "    plt.title('House Price Distributions')\n",
    "    plt.xlabel('House Area ')\n",
    "    plt.ylabel('House Price ')\n",
    "    plt.xlim(0,250)\n",
    "    plt.ylim(0,2500)\n",
    "    predict = plt.plot(x,y_predict,label='Predict')\n",
    "    plt.legend(loc='upper left')\n",
    "    plt.savefig('result1.png')\n",
    "    plt.show()\n",
    "\n",
    "data = np.loadtxt('./datasets/data.txt',delimiter = ',')\n",
    "plot_data(data)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从输出结果图可以看出，预测数据落在直线上，通过观察可以清楚地看到真实数据大部分散布在预测数据周围，说明预测结果是比较可靠的。\r\n",
    "\r\n",
    "你得到的预测图，应与下图相似：<img src='images/plt.png' style = \"width:400px;height:300px;\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5 - 总结\r\n",
    "\r\n",
    "通过这个练习我们应该记住：\r\n",
    "\r\n",
    "1. 机器学习的典型过程：\r\n",
    "    \r\n",
    "    - 获取数据\r\n",
    "    \r\n",
    "    - 数据预处理\r\n",
    "    \r\n",
    "    -训练模型\r\n",
    "    \r\n",
    "    -应用模型\r\n",
    "\r\n",
    "2. fluid训练模型的基本步骤: \r\n",
    "\r\n",
    "    - 配置网络结构：\r\n",
    "    \r\n",
    "    - 定义成本函数avg_cost\r\n",
    "    \r\n",
    "    - 定义优化器optimizer\r\n",
    "    \r\n",
    "    - 获取训练数据\r\n",
    "    \r\n",
    "    - 定义运算场所(place)和执行器(exe)\r\n",
    "    \r\n",
    "    - 提供数据(feeder)\r\n",
    "\r\n",
    "    - 执行训练(exe.run)\r\n",
    "\r\n",
    "    - 预测infer()并输出拟合图像\r\n",
    "\r\n",
    "3. 练习中的许多参数可以作调整，例如修改学习率会对模型结果产生很大影响，大家可以在本练习或者后面的练习中多做些尝试。\r\n",
    "\r\n",
    "\r\n",
    "至此线性回归模型的训练工作完成，希望通过本次课程的学习，读者可以利用提供的代码完成一个简单的房价预测模型。通过这一过程，初步了解PaddlePaddle这一易学易用的分布式平台。\r\n",
    "\r\n",
    "本节课作为PaddlePaddle的快速入门章节，希望可以开启您的下一步深度学习之门。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.5.0 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
